package action

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	"github.com/tidwall/sjson"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/query"
)

type Config struct {
	Orm *gorm.DB
}

func (action *Config) Execute(topic string, jsonData map[string]interface{}) {
	devSn := strings.Split(topic, "/")[3]
	_, to := AutoLogin(action.Orm, devSn, jsonData)
	code := 200
	msg := "success"
	if strings.Contains(topic, "/config/get") {
		reqPath := utils.GetFieldFromJson([]string{"data", "path"}, jsonData)
		path := utils.If(reqPath != nil, reqPath, "").(string)
		// 获取profile.config
		needSub := strings.HasSuffix(topic, "/subdevice")
		err, profilePathResult := getDevConfig(action.Orm, devSn, path, needSub)
		if err != nil {
			logger.Error(err)
			code = 500
			msg = err.Error()
		}
		// 响应
		ackTopic := fmt.Sprintf("/iot/%s/%s/config/get/ack", devSn, constant.TopicPlaceholder)
		ackData := map[string]interface{}{
			"sid":  jsonData["sid"],
			"to":   to,
			"from": constant.TopicPlaceholder,
			"code": code,
			"msg":  msg,
			"data": profilePathResult,
		}
		mqtt.MqttClient.Publish(ackTopic, utils.MapToBytes(ackData))
	} else if strings.Contains(topic, "/config/set") {
		dataObj := jsonData["data"]
		if dataObj == nil {
			return
		}
		dataStr := string(utils.MapToBytes(dataObj.(map[string]interface{})))
		sql := "update sys_dev_config set data = jsonb_set(data::jsonb, '{config}', jsonb_update(data #> '{config}', '" + dataStr + "' :: jsonb ) ) where dev_sn = '%s'"
		// 防止字符串中存在?占位符
		sql = fmt.Sprintf(sql, devSn)
		if err := action.Orm.Raw(sql).Scan(&models.SysDevConfig{}).Error; err != nil {
			code = 500
			msg = err.Error()
		}
		// 响应
		ackTopic := fmt.Sprintf("/iot/%s/%s/config/set/ack", devSn, constant.TopicPlaceholder)
		ackData := map[string]interface{}{
			"sid":  jsonData["sid"],
			"to":   to,
			"from": constant.TopicPlaceholder,
			"code": code,
			"msg":  msg,
		}
		mqtt.MqttClient.Publish(ackTopic, utils.MapToBytes(ackData))
		go func() {
			devSchema := &models.SysDevSchema{}
			devSchema.Orm = action.Orm
			devSchema.InferConfigSchemaMerge(devSn, dataObj.(map[string]interface{}))
		}()
	}
}

func (action *Config) Run(threadName string) {
	go func() {
		for {
			select {
			case <-time.After(10 * time.Second):
				result, _ := env.Cache.RPop(constant.LoraSlaveMqttConf)
				if len(result) > 0 {
					action.LoraSlaveConfigSet(utils.BytesToMap([]byte(result)))
				}
			}
		}
	}()
}

func getDevConfig(db *gorm.DB, sn, path string, needSub bool) (err error, result map[string]interface{}) {
	devConfig := &models.SysDevConfig{}
	devConfig.Model.Orm = db
	// 查询config
	if err = devConfig.FindOne(devConfig, cDto.MakeCondition(query.SysDevConfigQuery{Sn: sn})); err != nil {
		return
	}
	obj := utils.BytesToMap(devConfig.Data)["config"]
	if obj == nil {
		return
	}
	config := obj.(map[string]interface{})
	if needSub {
		// 网关自己的不要
		config = map[string]interface{}{}
		// 组装子设备config
		var subdeviceJson map[string]interface{}
		if subdeviceJson, err = devConfig.GetSubDeviceConfig(sn); err != nil {
			return
		}
		for k, v := range subdeviceJson {
			config[k] = v
		}
	}
	if len(path) > 0 {
		paths := strings.Split(path, ".")
		child := utils.GetFieldFromJson(paths, config)
		tmp := map[string]interface{}{}
		t(tmp, paths, 0, child)
		result = tmp
	} else {
		result = config
	}
	return
}

// 递归结果格式，适配终端协议定义
func t(m map[string]interface{}, paths []string, n int, final interface{}) {
	if n == len(paths)-1 {
		m[paths[n]] = final
		return
	}
	m[paths[n]] = map[string]interface{}{}
	t(m[paths[n]].(map[string]interface{}), paths, n+1, final)
}

func (action *Config) LoraSlaveConfigSet(mp map[string]interface{}) {
	// 找到lora网关sn
	dev := &models.SysDev{}
	dev.Model.Orm = action.Orm
	devlist := make([]models.SysDev, 0)
	if err := dev.FindList(&devlist, cDto.MakeCondition(query.SysDevQuery{ProductModel: "ssiot-gw-lora-power"})); err != nil {
		logger.Error(err)
		return
	}
	sns := make([]string, 0)
	for _, sysDev := range devlist {
		sns = append(sns, sysDev.Sn)
	}
	if len(sns) == 0 {
		return
	}
	var flag string
	var confMap map[string]interface{}
	for k, v := range mp {
		flag = k
		confMap = v.(map[string]interface{})
	}
	// 处理各网关config
	host := utils.GetStringValueFiled(confMap, "host")
	port := utils.GetStringValueFiled(confMap, "port")
	devconfig := &models.SysDevConfig{}
	devconfig.Model.Orm = action.Orm
	for _, sn := range sns {
		q := cDto.MakeCondition(query.SysDevConfigQuery{Sn: sn})
		if err := devconfig.FindOne(devconfig, q); err != nil {
			logger.Error(err)
			continue
		}
		config := string(devconfig.Data)
		m := utils.GetFieldFromJson([]string{"config", "ssiot", "mqtt"}, utils.BytesToMap(devconfig.Data))
		mqArr := make([]map[string]interface{}, 0)
		if m != nil {
			tmpArr := m.([]interface{})
			for _, tmp := range tmpArr {
				mn := tmp.(map[string]interface{})
				host1 := utils.GetStringValueFiled(mn, "host")
				port1 := utils.GetStringValueFiled(mn, "port")
				if host1 == host && port1 == port {
					continue
				}
				mqArr = append(mqArr, mn)
			}
		}
		if flag == "open" {
			mqArr = append(mqArr, confMap)
		}
		if len(mqArr) == 0 {
			logger.Error("不能清空lora mq配置")
			continue
		}
		config, _ = sjson.Set(config, "config.ssiot.mqtt", mqArr)
		if err := devconfig.ModMap(devconfig, map[string]interface{}{"data": []byte(config)}, q); err != nil {
			logger.Error(err)
			continue
		}
		// 下发各网关config
		configData := utils.BytesToMap([]byte(config))["config"].(map[string]interface{})
		topic := fmt.Sprintf("/iot/%s/%s/config/set", sn, constant.TopicPlaceholder)
		content := make(map[string]interface{})
		sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(100000)
		content["sid"] = sid
		content["from"] = constant.TopicPlaceholder
		content["to"] = ""
		content["data"] = configData
		contentBytes, _ := json.Marshal(content)
		mqtt.MqttClient.Publish(topic, contentBytes)
	}
}
